In [25]:
%matplotlib inline
from IPython.display import HTML
Image Encryption and Analysis using Chaos Map¶
1. Introduction¶
In this notebook, we perform image encryption using a chaotic map, followed by a detailed analysis of the results. The analysis includes:
- Histogram Analysis
- Differential Analysis (NPCR, UACI)
- Entropy, Correlation Coefficient (HVD)
- Robustness Analysis (Noise & Cropping Attacks)
- Computation Time for Encryption & Decryption
The encryption technique used is based on XOR encryption with a randomly generated key, and performance metrics are assessed to evaluate robustness and efficiency.
In [1]:
import numpy as np
import cv2
# === Load original image ===
image_path = r"D:\crypto project\Image\image8.tiff"
image = cv2.imread(image_path, cv2.IMREAD_COLOR)
if image is None:
raise FileNotFoundError("Original image not found")
# === Set a fixed seed and generate a random key ===
seed = 42
np.random.seed(seed)
key = np.random.randint(0, 256, size=image.shape, dtype=np.uint8)
# === Encrypt the image ===
encrypted_image = cv2.bitwise_xor(image, key)
cv2.imwrite(r"D:\crypto project\Image\encrypted_image.png", encrypted_image)
# === Decrypt the image ===
np.random.seed(seed) # Re-seed to generate the same key
key_for_decryption = np.random.randint(0, 256, size=image.shape, dtype=np.uint8)
decrypted_image = cv2.bitwise_xor(encrypted_image, key_for_decryption)
cv2.imwrite(r"D:\crypto project\Image\decrypted_image.png", decrypted_image)
# Show results
cv2.imshow("Encrypted Image", encrypted_image)
cv2.imshow("Decrypted Image", decrypted_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Key Hashing, Compression, Base64 Encoding, and QR Code Generation¶
In [2]:
import base64
import zlib
import hashlib
import qrcode
import numpy as np
import cv2
# Load original image
image = cv2.imread(r"D:\crypto project\Image\image8.tiff", cv2.IMREAD_COLOR)
if image is None:
raise FileNotFoundError("Input image not found")
# Generate per-pixel random key for encryption
np.random.seed(42)
key = np.random.randint(0, 256, size=image.shape, dtype=np.uint8)
# Generate SHA-256 hash of the encryption key (reduce size for storing in QR)
key_hash = hashlib.sha256(key).digest()
# Print the key hash to check its value
print("Original Key Hash (SHA-256):", key_hash)
# Compress the hash
compressed_key = zlib.compress(key_hash)
# Print the compressed key to check its size
print("Compressed Key:", compressed_key)
# Encode the compressed key to base64
base64_key = base64.b64encode(compressed_key).decode()
# Print base64 encoded key to check
print("Base64 Encoded Key:", base64_key)
# Generate the QR code
qr = qrcode.make(base64_key)
qr.save(r"D:\crypto project\Image\key_qr.png")
Original Key Hash (SHA-256): b'\x1b\xd6_\xf6\x18\xd3\xd32\r\x84)V-\xdb\xdeD\x94\xd0\x9e\x8a\x1d\xfd\xbcG\x01\xb5#"\xb2\xbbG\x8c'
Compressed Key: b'x\x9c\x93\xbe\x16\xffM\xe2\xf2e#\xde\x16\xcd0\xdd\xdb\xf7\\\xa6\\\x98\xd7%\xfbw\x8f;\xe3Ve\xa5M\xbb\xdd{\x00\xfe\xda\x0fU'
Base64 Encoded Key: eJyTvhb/TeLyZSPeFs0w3dv3XKZcmNcl+3ePO+NWZaVNu917AP7aD1U=
QR Code Reading and Seed Extraction¶
In [3]:
import cv2
import zlib
import base64
def read_qr_seed(qr_code_path):
# Read QR image
img = cv2.imread(qr_code_path)
if img is None:
raise FileNotFoundError("QR code image not found")
# Decode QR
detector = cv2.QRCodeDetector()
data, _, _ = detector.detectAndDecode(img)
if data:
# Decode from base64 and decompress
decoded_seed = zlib.decompress(base64.b64decode(data)).decode()
print("Decoded Seed from QR:", decoded_seed)
return int(decoded_seed)
else:
print("QR Code not detected!")
return None
# Path to saved QR code
qr_path = r"D:\crypto project\Image\key_seed_qr.png"
seed_from_qr = read_qr_seed(qr_path)
# Now you can regenerate the same key using this seed:
# np.random.seed(seed_from_qr)
# key = np.random.randint(0, 256, size=(image_height, image_width, 3), dtype=np.uint8)
Decoded Seed from QR: 42
Decode and Decompress QR Key¶
In [4]:
# Generate SHA-256 hash of the encryption key (reduce size for storing in QR)
key_hash = hashlib.sha256(key).digest()
# Print the key hash to check its value
print("Original Key Hash (SHA-256):", key_hash)
# Compress the hash
compressed_key = zlib.compress(key_hash)
# Print the compressed key to check its size
print("Compressed Key:", compressed_key)
# Encode the compressed key to base64
base64_key = base64.b64encode(compressed_key).decode()
# Base64 string from QR code (ensure this matches the printed value exactly)
qr_code_text = base64_key
# Step 1: Decode the base64 string
compressed_bytes = base64.b64decode(qr_code_text)
# Print the decoded bytes to check
print("Decoded Compressed Bytes:", compressed_bytes)
# Step 2: Decompress the data
try:
decompressed_key_hash = zlib.decompress(compressed_bytes)
print("Decompressed Key Hash:", decompressed_key_hash)
except Exception as e:
print("Decompression error:", e)
Original Key Hash (SHA-256): b'\x1b\xd6_\xf6\x18\xd3\xd32\r\x84)V-\xdb\xdeD\x94\xd0\x9e\x8a\x1d\xfd\xbcG\x01\xb5#"\xb2\xbbG\x8c'
Compressed Key: b'x\x9c\x93\xbe\x16\xffM\xe2\xf2e#\xde\x16\xcd0\xdd\xdb\xf7\\\xa6\\\x98\xd7%\xfbw\x8f;\xe3Ve\xa5M\xbb\xdd{\x00\xfe\xda\x0fU'
Decoded Compressed Bytes: b'x\x9c\x93\xbe\x16\xffM\xe2\xf2e#\xde\x16\xcd0\xdd\xdb\xf7\\\xa6\\\x98\xd7%\xfbw\x8f;\xe3Ve\xa5M\xbb\xdd{\x00\xfe\xda\x0fU'
Decompressed Key Hash: b'\x1b\xd6_\xf6\x18\xd3\xd32\r\x84)V-\xdb\xdeD\x94\xd0\x9e\x8a\x1d\xfd\xbcG\x01\xb5#"\xb2\xbbG\x8c'
Calculate the key size¶
In [5]:
# Calculate key size
key_bits = key.size * 8
key_bytes = key_bits // 8
key_kb = key_bytes / 1024
key_mb = key_kb / 1024
# Print results
print(f"Image Shape (H, W, C): {image.shape}")
print(f"Total Key Size: {key_bits} bits")
print(f"Total Key Size: {key_bytes} bytes")
print(f"Total Key Size: {key_kb:.2f} KB")
print(f"Total Key Size: {key_mb:.4f} MB")
Image Shape (H, W, C): (512, 512, 3) Total Key Size: 6291456 bits Total Key Size: 786432 bytes Total Key Size: 768.00 KB Total Key Size: 0.7500 MB
Key Correlation Analysis¶
In [9]:
def correlation_analysis(key):
# Reshape the key to 2D
key_flattened = key.reshape(-1, key.shape[-1])
# Calculate correlation between channels
corr_matrix = np.corrcoef(key_flattened.T) # Transpose to get correlation between channels
return corr_matrix
# Get correlation matrix
corr_matrix = correlation_analysis(key)
# Plot the correlation matrix
plt.imshow(corr_matrix, cmap='coolwarm', interpolation='none')
plt.colorbar()
plt.title("Correlation Matrix of Key Channels")
plt.xticks([0, 1, 2], ['Blue', 'Green', 'Red'])
plt.yticks([0, 1, 2], ['Blue', 'Green', 'Red'])
plt.show()
3D Visualization of Key Values¶
In [10]:
from mpl_toolkits.mplot3d import Axes3D
# Reshape key into a 2D array (height * width) x 3 (RGB)
key_flattened = key.reshape((-1, 3))
# Create a 3D scatter plot for RGB channels
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(key_flattened[:, 0], key_flattened[:, 1], key_flattened[:, 2], c=key_flattened / 255.0, s=0.5)
ax.set_xlabel('Red')
ax.set_ylabel('Green')
ax.set_zlabel('Blue')
ax.set_title('3D Key Value Distribution')
plt.show()
In [13]:
# Decrypt the image
decrypted_image = cv2.bitwise_xor(encrypted_image, key)
# Show encrypted vs decrypted comparison
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1); plt.imshow(cv2.cvtColor(encrypted_image, cv2.COLOR_BGR2RGB)); plt.title("Encrypted Image")
plt.subplot(1, 2, 2); plt.imshow(cv2.cvtColor(decrypted_image, cv2.COLOR_BGR2RGB)); plt.title("Decrypted Image")
plt.tight_layout(); plt.show()
Encrypting the images using discrete hyperchaotic maps¶
Using 3D Hyperchaotic map¶
In [2]:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Improved 3D hyperchaotic map
def hyperchaotic_map(x, y, z, a=35, b=3, c=28):
dt = 0.01
x_next = x + a * (y - x) * dt
y_next = y + (x * (c - z) - y) * dt
z_next = z + (x * y - b * z) * dt
return x_next, y_next, z_next
# Generate chaotic sequence for image dimensions and 3 channels
def generate_chaotic_sequence(image_shape):
h, w, c = image_shape
x, y, z = 0.1, 0.1, 0.1 # Initial conditions
sequence = np.zeros((h, w, c), dtype=np.uint8)
for i in range(h):
for j in range(w):
x, y, z = hyperchaotic_map(x, y, z)
val = int(((abs(x) + abs(y) + abs(z)) * 10000) % 256)
sequence[i, j] = [val, val, val] # Same chaotic value for all channels
return sequence
# XOR Encryption/Decryption
def encrypt_decrypt_image(image, chaotic_sequence):
return cv2.bitwise_xor(image, chaotic_sequence)
# Load original color image
image_path = 'D:/crypto project/Image/image8.tiff'
image = cv2.imread(image_path)
if image is None:
raise FileNotFoundError("Image not found")
# Generate chaotic key stream
chaotic_sequence = generate_chaotic_sequence(image.shape)
# Encrypt
encrypted_image = encrypt_decrypt_image(image, chaotic_sequence)
# Decrypt
decrypted_image = encrypt_decrypt_image(encrypted_image, chaotic_sequence)
# Convert from BGR to RGB for displaying with matplotlib
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
encrypted_rgb = cv2.cvtColor(encrypted_image, cv2.COLOR_BGR2RGB)
decrypted_rgb = cv2.cvtColor(decrypted_image, cv2.COLOR_BGR2RGB)
# Display results
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.imshow(image_rgb)
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 3, 2)
plt.imshow(encrypted_rgb)
plt.title('Encrypted Image')
plt.axis('off')
plt.subplot(1, 3, 3)
plt.imshow(decrypted_rgb)
plt.title('Decrypted Image')
plt.axis('off')
plt.tight_layout()
plt.show()
In [18]:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Load encrypted image
encrypted_image = cv2.imread(r"D:\crypto project\Image\encrypted_image.png", cv2.IMREAD_COLOR)
if encrypted_image is None:
raise FileNotFoundError("Encrypted image not found")
# Separate RGB channels of the encrypted image
channels = ['b', 'g', 'r']
plt.figure(figsize=(8, 6))
for i, col in enumerate(channels):
hist = cv2.calcHist([encrypted_image], [i], None, [256], [0, 256]) # Histogram calculation
plt.plot(hist, color=col, label=col)
plt.title("Histogram of Encrypted Image (RGB)")
plt.xlabel('Pixel Intensity')
plt.ylabel('Frequency')
plt.legend(loc='upper right')
plt.grid(True)
plt.show()
In [19]:
# Plot each channel histogram separately for more visual clarity
for i, col in enumerate(channels):
plt.figure(figsize=(6, 4))
hist = cv2.calcHist([encrypted_image], [i], None, [256], [0, 256])
plt.plot(hist, color=col)
plt.title(f"{col.upper()} Channel Histogram of Encrypted Image")
plt.xlabel('Pixel Intensity')
plt.ylabel('Frequency')
plt.grid(True)
plt.tight_layout()
plt.show()
Histogram Comparison Plot¶
In [20]:
import matplotlib.pyplot as plt
fig, axs = plt.subplots(1, 2, figsize=(10, 4))
original_image = cv2.imread("D:/crypto project/Image/image8.tiff")
axs[0].hist(original_image.flatten(), bins=256, color='blue')
axs[0].set_title("Original Histogram")
axs[1].hist(encrypted_image.flatten(), bins=256, color='red')
axs[1].set_title("Encrypted Histogram")
plt.tight_layout()
plt.show()
Histogram Comparison of Original, Encrypted, and Decrypted¶
In [21]:
import cv2
import matplotlib.pyplot as plt
# Load all images
original_image = cv2.imread(r"D:\crypto project\Image\image8.tiff")
encrypted_image = cv2.imread(r"D:\crypto project\Image\encrypted_image.png")
decrypted_image = cv2.imread(r"D:\crypto project\Image\decrypted_image.png")
# Convert from BGR to RGB for plotting
original_image_rgb = cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB)
encrypted_image_rgb = cv2.cvtColor(encrypted_image, cv2.COLOR_BGR2RGB)
decrypted_image_rgb = cv2.cvtColor(decrypted_image, cv2.COLOR_BGR2RGB)
# Plot histograms for each color channel
def plot_histogram(image, title):
color = ('r', 'g', 'b')
for i, col in enumerate(color):
hist = cv2.calcHist([image], [i], None, [256], [0, 256])
plt.plot(hist, color=col)
plt.xlim([0, 256])
plt.title(title)
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.grid(True)
plt.figure(figsize=(18, 5))
plt.subplot(1, 3, 1)
plot_histogram(original_image_rgb, "Original Image Histogram")
plt.subplot(1, 3, 2)
plot_histogram(encrypted_image_rgb, "Encrypted Image Histogram")
plt.subplot(1, 3, 3)
plot_histogram(decrypted_image_rgb, "Decrypted Image Histogram")
plt.tight_layout()
plt.show()
In [22]:
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
def animated_histogram(image1, image2, title1, title2):
# Check if images are loaded correctly
if image1 is None or image2 is None:
print("Error: One or both images are not loaded correctly.")
return
# Print image shapes and types to verify
print(f"Image 1 shape: {image1.shape}, dtype: {image1.dtype}")
print(f"Image 2 shape: {image2.shape}, dtype: {image2.dtype}")
# Convert images to grayscale if they are colored
if len(image1.shape) == 3:
image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
if len(image2.shape) == 3:
image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
# Calculate histograms
bins = np.arange(256)
hist1 = cv2.calcHist([image1], [0], None, [256], [0, 256]).flatten()
hist2 = cv2.calcHist([image2], [0], None, [256], [0, 256]).flatten()
# Create plot
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_xlim(0, 256)
ax.set_ylim(0, max(np.max(hist1), np.max(hist2)) + 10)
line1, = ax.plot([], [], label=f'{title1} (Original)', color='blue', alpha=0.5)
line2, = ax.plot([], [], label=f'{title2} (Encrypted)', color='red', alpha=0.5)
ax.set_title(f"Pixel Intensity Distribution: {title1} vs {title2}")
ax.set_xlabel('Pixel Intensity')
ax.set_ylabel('Frequency')
ax.legend(loc='best')
def update(frame):
line1.set_data(bins[:frame], hist1[:frame])
line2.set_data(bins[:frame], hist2[:frame])
return line1, line2
ani = animation.FuncAnimation(fig, update, frames=256, interval=30, blit=True)
plt.close(fig) # Prevent double plot in Jupyter
return HTML(ani.to_jshtml())
# Load images
original_image = cv2.imread(r"D:\crypto project\Image\image8.tiff")
encrypted_image = cv2.imread(r"D:\crypto project\Image\encrypted_image.png")
# Show animated histogram in Jupyter
animated_histogram(original_image, encrypted_image, "Original", "Encrypted")
Image 1 shape: (512, 512, 3), dtype: uint8 Image 2 shape: (512, 512, 3), dtype: uint8
Out[22]: